#!/usr/bin/env python

# This file is part of the Possumcam project 
# at http://code.google.com/p/possumcam/
#
# Released under the GPLv3 at http://www.gnu.org/copyleft/gpl.html
# Designed for Kenneth Lavrsen's motion project at
# http://www.lavrsen.dk/twiki/bin/view/Motion
#
# Copyright 2008 by Jackson Yee (jackson@gotpossum.com)

from os.path import join
import os
import sys
import gc
import time
import datetime
import subprocess

from wsgiref.util import setup_testing_defaults, request_uri
from wsgiref.headers import Headers
from wsgiref.simple_server import make_server

from lumenn.files import FileList, FileInfo
from lumenn.db import DB, ListAdapter
import lumenn.log as log

execfile('webserver.config')

db					=	DB( 'filesdb' )
videos			=	ListAdapter(db, FileInfo)

c200  = '200 OK'
c503  = '503 Service Unavailable'

styles  = """
"""

pageheader  = """
<div class="Header">
<div class="Logo">
<a href="http://www.gotpossum.com"><img src="/possum.logo-modern-gradient.png" width="80" height="70" /></a>
</div>
<div class="Motto"></div>
<div class="LogoText">PossumCam - %s</div>
</div>
""" % sitename

# =====================================================================
def TabBar(action):
	ls	=	['<div class="Tabs">', ]
	
	ls.append('<a href="/livecams" ')
	
	if action == 'livecams':
		ls.append('class="Selected" ')
	
	ls.append('>Live Cameras</a>')
	
	ls.append('<a href="/recorded" ')
	
	if action == 'recorded':
		ls.append('class="Selected" ')
	
	ls.append('>Recorded Videos</a>')
	
	ls.append('<a href="/status" ')
	
	if action == 'status':
		ls.append('class="Selected" ')
	
	ls.append('>System Status</a>')
	
	ls.append('</div>')
	
	return ''.join(ls)

# =====================================================================
def LiveCamScript():
	ls	=	[]
	
	for n in cams:
		ls.append("document.images.cam%s.src = '/cam/%s?' + now;\n" % (n, n) )
		
	return ''.join(ls)

# =====================================================================
def LiveCamImages():
	ls	=	[]
	
	for n in cams:
		ls.append("""<div class="LiveImage">
			<img src="/cam/%s" width="%s" height="%s" name="cam%s" /><br />
			%s
			</div>"""
			% (
				n, 
				camres[n][0],
				camres[n][1],
				n,
				camnames[n]
			) 
		)
		
	return ''.join(ls)
	
# =====================================================================
def LiveCams(respond, headers):
	ret = """
<html>
<head>
<title>PossumCam - Live Cameras - %s</title>
	<style type="text/css" media="all">
		@import "/default.css";
	</style>
<script type="text/javascript">

function ReloadImages() 
{
	var now = new Date();
	now	=	now.getTime()
	
	if (document.images) 
	{
		%s
	}
}

setInterval('ReloadImages()', %s);

</script>
</head>
<body>
%s
%s
<div class="Body">
	%s
</div>
</body>
</html>""" % (
		sitename, 
		LiveCamScript(), 
		refreshtime, 
		pageheader, 
		TabBar('livecams'), 
		LiveCamImages(),
	)
	
	headers.add_header('content-type', 'text/html')
	headers.add_header('content-length', str( len(ret) ) )
	respond(c200, headers.items() )
	return ret

# =====================================================================
def CalendarBar(year, month, day):
	d	=	datetime.date(year, month, 1)
	now	=	datetime.date.today()
	
	ls = ['<table class="Calendar">',]
	
	ls.append( '<tr class="Year"><td>' )
	
	if month == 1:
		videos.SetFilter('WHERE filename LIKE "%%-%d12%%"' % (year - 1) )
		if not videos.IsEmpty():
			ls.append('<a href="/recorded/%s/%s">&lt;&lt;</a>' % (year - 1, 12) )
	else:
		videos.SetFilter('WHERE filename LIKE "%%-%d%02d%%"' % (year, month - 1) )
		
		if not videos.IsEmpty():
			ls.append('<a href="/recorded/%s/%s">&lt;&lt;</a>' % (year, month - 1) )
	
	ls.append( '</td><td>%s<br />%s</td><td>' % (d.strftime('%B'), year) )
	
	if month == 12:
		videos.SetFilter('WHERE filename LIKE "%%-%d01%%"' % (year + 1) )
		if not videos.IsEmpty():
			ls.append('<a href="/recorded/%s/%s">&gt;&gt;</a>' % (year + 1, 1) )
	else:
		videos.SetFilter('WHERE filename LIKE "%%-%d%02d%%"' % (year, month + 1) )
		
		if not videos.IsEmpty():
			ls.append('<a href="/recorded/%s/%s">&gt;&gt;</a>' % (year, month + 1) )
		
	ls.append( '</td></tr>' )
	
	d1	=	datetime.timedelta(1)
	
	while True:
		videos.SetFilter('WHERE filename LIKE "%%-%d%02d%02d%%"' % (year, month, d.day) )
		
		if not videos.IsEmpty():
			ls.append('<tr ')
		
			if day == now.day and month == now.month and year == now.year:
				ls.append( 'class="CurrentDay" ' )
		
			ls.append("""><td colspan="3">
				<a href="/recorded/%s/%s/%s">%s</a>
				</td></tr>
			""" % (year, month, d.day, d.day))
		
		d += d1
		
		if d.day == 1:
			break;	
	
	ls.append( '</table>' )
	
	return ''.join(ls)

# =====================================================================
def VideosBar(year, month, day):
	d	=	datetime.date(year, month, day)
	
	ls	=	['<h1>Video Listing for %s</h1>' % d.strftime('%x'), ]
	
	videos.SetFilter('WHERE filename LIKE "%%-%d%02d%02d%%" ORDER BY filename' % (year, month, day) )
	
	if videos.IsEmpty():
		ls.append('<p>No videos found. Please try another date.</p>')
		return ''.join(ls)
	
	hours	=	{}
	
	for v in videos:
		parts	=	v.filename.split('-')
		rectime	=	parts[1]
		camnum	=	int( parts[0] )
		hour		=	int( rectime[8:10] )
		minute	=	int( rectime[10:12] )
		second	=	int( rectime[12:14] )
		
		if hour not in hours:
			hours[hour]	=	{}
		
		if camnum not in hours[hour]:
			hours[hour][camnum]	=	[]
			
		hours[hour][camnum].append( (minute, second, v.filename) )
	
	ls.append("""<table class="VideoList">
		<tr>
			<td></td>
	""")
	
	for n in cams:
		ls.append('<td>%s</td>\n' % camnames[n])
	
	ls.append('</tr>')
	
	for h in xrange(0, 24):
		if h not in hours:
			continue
		
		ls.append('<tr><td>')
		
		if h > 12:
			ls.append('%d PM' % (h - 12) )
		else:
			if h == 0:
				ls.append('12 AM')
			else:
				ls.append('%d AM' % h)
		
		ls.append('</td>')
		
		for n in cams:
			ls.append('<td>')
			
			if n in hours[h]:
				for v in hours[h][n]:
					ls.append('<a href="/videos/%s">%d:%02d:%02d</a><br />' 
						% (
							v[2],
							h,
							v[0],
							v[1],
						)
					)
			
			ls.append('&nbsp;</td>')
		
		ls.append('</tr>')		
	
	ls.append('</table>')
	
	return ''.join(ls)	

# =====================================================================
def RecordedVideos(args, respond, headers):
	now	=	time.time()
	
	d	=	datetime.date.today()
	
	year	=	d.year
	month	=	d.month
	day		=	d.day
	
	l	=	len(args)
	
	if l > 0:
		year	=	int(args[0])
	
	if l > 1:
		month	=	int(args[1])
		
	if l > 2:
		day	=	int(args[2])
	
	ret = """
<html>
<head>
<title>PossumCam - Recorded Videos - %s</title>
	<style type="text/css" media="all">
		@import "/default.css";
	</style>
<script type="text/javascript">
</script>
</head>
<body>
%s
%s
<div class="Body">
<table class="Videos">
<tr><td valign="top">%s</td><td valign="top">%s</td></tr>
</table>
</div>
</body>
</html>""" % (
		sitename, 
		pageheader, 
		TabBar('recorded'), 
		CalendarBar(year, month, day),
		VideosBar(year, month, day),
	)
	
	headers.add_header('content-type', 'text/html')
	headers.add_header('content-length', str( len(ret) ) )
	
	respond(c200, headers.items() )
	return str(ret)

# =====================================================================
def SystemBar():
	ls	=	['<pre>', ]
	
	ls.append( subprocess.Popen(['df', '-h'], 
			stdout = subprocess.PIPE
		).communicate()[0] 
	)
	
	ls.append('\n\n')
	
	ls.append( subprocess.Popen(['dstat', '-cdmns', '1', '3', ], 
			stdout = subprocess.PIPE
		).communicate()[0] 
	)
	
	ls.append('\n\n')
	
	ls.append('</pre>')
	return ''.join(ls)

# =====================================================================
def SystemStatus(args, respond, headers):
	ret = """
<html>
<head>
<title>PossumCam - System Status - %s</title>
	<style type="text/css" media="all">
		@import "/default.css";
	</style>
<script type="text/javascript">
</script>
</head>
<body>
%s
%s
<div class="Body">
%s
</div>
</body>
</html>""" % (
		sitename, 
		pageheader, 
		TabBar('status'), 
		SystemBar(),
	)
	
	headers.add_header('content-type', 'text/html')
	headers.add_header('content-length', str( len(ret) ) )
	respond(c200, headers.items() )
	return ret

# =====================================================================
def GetVideo(args, respond, headers):
	try:
		p = open( join(videosdir, args[0]), 'rb' )
		p.seek(0, os.SEEK_END)
		
		length  = p.tell()
		
		p.seek(0, os.SEEK_SET)
		
		headers.add_header('content-type', 'video/')		
			
		headers.add_header('content-length', str(length) )
		headers.add_header('content-disposition', 'attachment', file = args[0])
		
		respond(c200, headers.items() )
		
		return p
	except Exception, e:
		respond(c503, [])
		log.Log(log.ERROR, '*** Problem loading file %s: %s\n' % (path, e) )
		return ['This file is currently unavailable: %s\n' % e, ]

# =====================================================================
def LoadFile(path, respond, headers):
	try:
		p = open( path, 'rb' )
		p.seek(0, os.SEEK_END)
		
		length  = p.tell()
		
		p.seek(0, os.SEEK_SET)
		
		if '.png' in path:		
			headers.add_header('content-type', 'image/png')		
		elif '.css' in path:
			headers.add_header('content-type', 'text/css')
			
		headers.add_header('content-length', str(length) )
		
		respond(c200, headers.items() )
		
		return p
	except Exception, e:
		respond(c503, [])
		log.Log(log.ERROR, '*** Problem loading file %s: %s\n' % (path, e) )
		return ['This file is currently unavailable: %s\n' % e, ]

# =====================================================================
def CamPicture(args, respond, headers):
	try:
		camnum = int( args[0] )
		
		p = open( join(videosdir, 'snapshot-%s.jpg' % camnum), 'rb' )
		p.seek(0, os.SEEK_END)
		length  = p.tell()
		p.seek(0, os.SEEK_SET)
		log.Log(log.ACTION, 'Cam %s picture found with length %s\n' % (camnum, length ) )
		headers.add_header('content-type', 'image/jpeg')
		headers.add_header('content-length', str(length) )
		respond(c200, headers.items() )
		return p
	except Exception, e:
		respond(c503, [])
		log.Log(log.ACTION, '*** Problem loading camera number %s: %s\n' % (camnum, e) )
		return ['This camera is currently unavailable: %s\n' % e, ]

# =====================================================================
def CamServer(env, respond):
	setup_testing_defaults(env)
	
	path  = env['PATH_INFO']
	parts	=	path.split('/')
	
	action	=	None
	args		=	[]
	
	if len(parts) > 1:
		action	=	parts[1]
		args		=	parts[2:]
	
	headers = Headers([])
	headers.add_header('content-type', 'text/plain')
	
	camnum  = -1
	
	if action == 'cam':
		return CamPicture( args, respond, headers)
	elif action == 'recorded':
		return RecordedVideos( args, respond, headers )
	elif action == 'status':
		return SystemStatus( args, respond, headers )
	elif action == 'videos':
		return GetVideo( args, respond, headers )
	
	if '.png' in path or '.css' in path:
		return LoadFile(parts[1], respond, headers)
	
	return LiveCams(respond, headers)

# =====================================================================
def Run():
	httpd = make_server('', serverport, CamServer)
	log.Log(log.ACTION, 'Starting webcam server on port %d...\n' % serverport)
	httpd.serve_forever()

if __name__ == '__main__':
    Run()

